//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Στο Node.js τα global αντικείμενα, για να έχουν εμβέλεια και σε άλλα αρχεία
// κώδικα που συμπεριλαμβάνονται με "require", πρέπει να ενταχθούν στο μόνο εξ
// ορισμού τέτοιο (global) αντικείμενο που παρέχεται και το οποίο ονομάζεται,
// πώς αλλιώς, "global". Αυτό είναι καλό και βοηθάει στην εξατομίκευση των επί
// μέρους global αντικειμένων που μπορούν ορίζονται με "var" μέσα στα διάφορα
// modules. Το πρόθεμα "global" μπορεί να παραλείπεται στον server, όπως ακριβώς
// μπορεί να παραλείπεται στον client το πρόθεμα "window".

// Υπάρχουν διάφορα global αντικείμενα που χρησιμοποιούντα στα JavaScript προγράμματα
// που είναι κοινά στον server και στους clients. Αυτά φροντίζουμε να τα εντάξουμε στο
// sigleton "globals" που χρησιμοποιείται ως namespace.

globals = {
	// Ακολουθεί η πόρτα στη οποία ο skiser ακούει service requests. Αυτή η πόρτα
	// πρέπει να είναι γνωστή και στον server και στους clients.

	servicePort: 8911,

	// Ακολουθεί η πόρτα στη οποία ο skiser ακούει monitor requests. Αυτή η πόρτα
	// πρέπει να είναι γνωστή και στον server και στους clients.

	monitorPort: 8912,
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Διάφορα global αντικείμενα που χρησιμοποιούνται μόνο στον server θα ενταχθούν στο
// singleton "server", το οποίο αρχικά θέτουμε false ώστε να μπορεί να χρησιμοποιηθεί
// και σε ελέγχους προσανατολισμού.

server = null;

// Διάφορα global αντικείμενα που χρησιμοποιούνται μόνο στους clients θα ενταχθούν στο
// singleton "client", το οποίο αρχικά θέτουμε false ώστε να μπορεί να χρησιμοποιηθεί
// και σε ελέγχους προσανατολισμού.

client = null;

// Η μέθοδος "isServer" επιστρέφει το αντικείμενο "server" αν το πρόγραμμα εκτελείται
// στον server, αλλιώς επιστρέφει false.

globals.isServer = function() {
	return server;
}

// Η μέθοδος "isClient" επιστρέφει το ανιτκείμενο "client" αν το πρόγραμμα εκτελείται
// στον client, αλλιώς επιστρέφει false.

globals.isClient = function() {
	return client;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "walk" διατρέχει όλα τα στοιχεία μιας λίστας και για κάθε στοιχείο
// καλεί κάποια function με παραμέτρους το κλειδί τού ανά χείρας στοιχείου και το
// ίδιο το στοιχείο της λίστας. Η μέθοδος επιστρέφει την ίδια τη λίστα.
// λίστας.

globals.walk = function(list, callback) {
	var i;

	for (i in list) {
		if (callback) callback(i, list[i]);
	}

	return list;
}

// Κάποιες φορές συμβαίνουν σφάλματα που οδηγούν σε αδιέξοδο και η ροή του προγράμματος
// θα πρέπει να διακοπεί. Σ' αυτές τις περιπτώσεις καλούμε την function "fatal", η οποία
// διακόπτει τη ροή του προγράμματος εκτυπώνοντας μήνυμα λάθους. Αν η function εκτελείται
// σε client, τότε εκτυπώνεται το μήνυμα και στη σελίδα.

globals.fatal = function(errmsg) {
	if (globals.isClient()) client.provlima(errmsg);
	throw new Error(errmsg);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Στην παρούσα ενότητα έχουμε functions που αφορούν στα timestamps.

// Η function "torams" επιστρέφει το τρέχον timestamp σε milliseconds,
// με όρους της μηχανής στην οποία εκτελείται.

globals.torams = function() {
	return (new Date).getTime();
}

// Η function "tora" επιστρέφει το τρέχον timestamp σε seconds,
// με όρους της μηχανής στην οποία εκτελείται.

globals.tora = function() {
	return parseInt((new Date).getTime() / 1000);
}

// Η function "toramsServer" επιστρέφει το τρέχον timestamp
// σε milliseconds, με όρους του server.

globals.toramsServer = function() {
	return globals.torams() + (client ? client.timeDif * 1000 : 0);
}

// Η function "toraServer" επιστρέφει το τρέχον timestamp
// σε seconds, με όρους του server.

globals.toraServer = function() {
	return globals.tora() + (client ? client.timeDif : 0);
}

// Η function "pote" δέχεται ένα timestamp και το επιστρέφει ως μήνυμα
// που περιγράφει το timestamp ως χρονική στιγμή σε σχέση με την τρέχουσα
// ώρα στη μηχανή που τρέχει.

globals.pote = function(ts) {
	var dif = globals.tora()- ts, x;

	if (dif < 60) return 'τώρα';

	if (dif < 3600) {
		x = Math.floor(dif / 60);
		return 'πριν ' + x + ' λεπτ' + (x < 2 ? 'ό' : 'ά');
	}

	if (dif < 86400) {
		x = Math.floor(dif / 3600);
		return 'πριν ' + x + ' ώρ' + (x < 2 ? 'α' : 'ες');
	}
	
	ts = new Date(ts * 1000);
	return globals.mera(ts) + ', ' + globals.ora(ts);
}

// Η function "mera" δίνει την τρέχουσα ημερονηνία στη μηχανή που τρέχει.
// Μπορούμε να δώσουμε και συγκεκριμένη ώρα ως παράμετρο.

globals.mera = function(d) {
	var s = '', x;
	if (d === undefined) d = new Date;

	x = d.getDate();
	if (x < 10) s += '0';
	s += x;
	s += '/';

	x = d.getMonth() + 1;
	if (x < 10) s += '0';
	s += x;
	s += '/'; 

	x = d.getFullYear();
	if (x < 2000) s += x;
	else {
		x %= 100;
		if (x < 10) s += '0';
		s += x;
	}

	return s;
}

// Η function "ora" δίνει την τρέχουσα ώρα στη μηχανή που τρέχει.
// Μπορούμε να δώσουμε και συγκεκριμένη ώρα ως παράμετρο.

globals.ora = function(d) {
	var s = '', x;
	if (d === undefined) d = new Date;

	x = d.getHours();
	if (x < 10) s += '0';
	s += x + ':';

	x = d.getMinutes();
	if (x < 10) s += '0';
	s += x + ':';

	x = d.getSeconds();
	if (x < 10) s += '0';
	s += x;

	return s;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "initObj" χρησιμοποιείται συνήθως στην αρχικοποίηση αντικειμένων.
// Δέχεται ένα αντικείμενο και μια λίστα και θέτει τα στοιχεία τής λίστας ως
// properties του αντικειμένου. Αν το αντικείμενο είχε κάποια properties πριν
// την εφαρμογή τής μεθόδου, αυτά τα properties διατηρούνται. By default τα
// properties τύπου function δεν αντιγράφονται, μπορούμε όμως να αντιγράψουμε
// και αυτά δίνοντας κατάλληλες options στην τρίτη παράμετρο.

globals.initObject = function(obj, props, opt) {
	var i;

	if (opt === undefined) opt = {
		functions: false,
		recursive: true,
	};

	for (i in props) {
		if (props[i] === null) obj[i] = null;
		else if (typeof props[i] === 'function') {
			if (opt.functions) obj[i] = props[i];
		}
		else if (typeof props[i] !== 'object') obj[i] = props[i];
		else if (opt.recursive) {
			if (props[i] instanceof Array) obj[i] = globals.initObject([], props[i]);
			else obj[i] = globals.initObject({}, props[i]);
		}
		else obj[i] = props[i];
	}

	return obj;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Ακολουθούν βολικές μέθοδοι που αφορούν σε strings.

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, '');
}

String.prototype.ltrim = function() {
	return this.replace(/^\s+/, '');
}

String.prototype.rtrim = function() {
	return this.replace(/\s+$/, '');
}

// Η κλήση που ακολουθεί εξυπηρετεί στη λειτουργία άλλων μεθόδων.

;(function() {
	var ipOctet = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])';
	globals.ipRE = new RegExp('^(?:' + ipOctet + '\\.){3}' + ipOctet + '$');
	globals.emailRE = new RegExp('^[a-zA-Z0-9_\.-]+\@([a-zA-Z0-9-]+\.)+([a-zA-Z0-9]{2,4})+$');
	globals.loginRE = new RegExp('^[a-zA-Z][a-zA-Z0-9_!@#=.:+-]*$');
})();

// Η μέθοδος "validIp" ελέγχει ένα string ως προς το εαν είναι
// είναι δεκτό IP. Αν ναι το επιστρέφει, αλλιώς επιστρέφει κενό
// string.

String.prototype.validIp = function() {
	return this.match(globals.ipRE) ? this + '' : '';
}

// Η μέθοδος "validEmail" ελέγχει ένα string ως email address. Αν
// είναι δεκτό το επιστρέφει, αλλιώς επιστρέφει κενό string.

String.prototype.validEmail = function() {
	return this.match(globals.emailRE) ? this : '';
}

// Η μέθοδος "validLogin" ελέγχει ένα string ως login name. Αν
// είναι δεκτό το επιστρέφει, αλλιώς επιστρέφει κενό string.

String.prototype.validLogin = function() {
	return this.match(globals.loginRE) ? this : '';
}

// Η μέθοδος "uri το μετατρέπει ένα string σε μορφή ασφαλή
// ώστε να χρησιμοποιηθεί ως URI component.

String.prototype.uri = function() {
	return encodeURIComponent(this);
}

// Η μέθοδος "htmlSafe" μετατρέπει ένα string σε μορφή ασφαλή ώστε
// να μπορεί ενταχθεί παραθετικά σε HTML χωρίς να μεταφραστούν
// τα HTML tags που τυχόν περιέχει.

String.prototype.htmlSafe = function() {
	return this.replace(/&/g, '&amp;').replace(/</g, '&lt;');
}

// Η μέθοδος "scriptSafe" μετατρέπει ένα string σε μορφή ασφαλή ώστε
// να μπορεί ενταχθεί παραθετικά σε HTML χωρίς να μεταφραστούν
// τα script HTML tags που τυχόν περιέχει.

String.prototype.scriptSafe = function() {
	return this.replace(/script/ig, '&#8203;script');
}

String.prototype.isNai = function() {
	return(this == 'ΝΑΙ');
}

String.prototype.oxiNai = function() {
	return !this.isNai();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "json" δέχεται μια παράμετρο και την μετατρέπει σε
// μορφή ασφαλή ώστε να χρησιμοποιηθεί ως rvalue σε δομές json.
// Η function δεν δίδεται ως string method ώστε να δύναται να
// χρησιμοποιηθεί σε οποιαδήποτε μεταβλητή και όχι μόνο σε strings.

globals.json = function(s) {
	var err = false;
	if (s === undefined) err = 'undefined data';
	else if (s === null) err = 'null data';
	else {
		switch (typeof s)  {
		case 'number':
			return s;
		case 'string':
			var x = s.replace(/\\/g, '\\\\');
			return "'" + x.replace(/'/g, '\\\'') + "'";
		default:
			err = s + ': invalid data type';
			break;
		}
	}

	globals.fatal('globals.json: ' + err);
}

// Η μέθοδος "random" επιστρέφει έναν τυχαίο ακέραιο μεταξύ των τιμών που δίνονται
// ως παράμετροι (inclusive). Π.χ. η κλήση globals.random(5, 10) μπορεί να δώσει 5,
// 6, 7, 8, 9 και 10.

globals.random = function(min, max) {
	switch (arguments.length) {
	case 0:
		min = 0;
		max = 999999999;
		break;
	case 1:
		max = min;
		min = 0;
		break;
	}

	return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Η μέθοδος "randomString" επιστρέφει ένα τυχαίο string με μήκος που καθορίζεται
// από την σχετική παράμετρο. Το string αποτελείται από γράμματα του λατινικού αλφαβήτου,
// αλλά αν θέλουμε μπορούμε να περάσουμε αυθαίρετο string από το οποίο θα επιλεγούν
// χαρακτήρες.

globals.randomString = function(len, pool) {
	if (pool === undefined) pool = 'abcdefghijklmnopqrstuvwxyz';
	var ret = '';
	while (len-- > 0) ret += pool.substr(globals.random(pool.length - 1), 1);
	return ret;
}

globals.apostasiTL = function(p1, p2) {
	var dx = p1.left - p2.left, dy = p1.top - p2.top;
	return Math.sqrt((dx * dx) + (dy * dy));
}
